#include "stdafx.h"

#include <MyFC/Range.h>
#include <algorithm>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

// Range

Range::Range()
{
	m_Size = 0;
	m_InvalidRow = INT_MAX;
	m_Invalid = false;
}

Size Range::GetSize() const
{
	if (!IsValid())
		const_cast<Range &>(* this).UpdateItems();
	return m_Size;
}

Span Range::GetBounds() const
{
	return Span(0, int(GetSize())).Offset(GetOffset());
}

bool Range::IsEmpty() const
{ 
	return m_Items.empty(); 
}

Size Range::GetItemCount() const
{
	return m_Items.size();
}

RangeItem & Range::GetItem(Size i) const
{
	assert(i < m_Items.size());
	return * m_Items[i];
}

namespace {

bool ItemIsBefore(RangeItem * pItem, int x) 
{
	return pItem->GetBounds().last <= x;
}

}

int Range::ItemIndexAt(int x) const
{
	if (x < GetOffset())
		return -1;
	std::vector<RangeItem *>::const_iterator it = 
		std::lower_bound(m_Items.begin(), m_Items.end(), 
			x, ItemIsBefore);
	return int(it - m_Items.begin());
}

void Range::Insert(Size pos, RangeItem * items[], Size count)
{
	if (count == 0)
		return;
	assert(pos <= m_Items.size());
	m_Items.insert(m_Items.begin() + pos, items, items + count);
	for (Size i = pos; i < pos + count; ++i)
		m_Items[i]->m_pRange = this;
	InvalidateItems(pos);
}

void Range::Remove(Size pos, Size count)
{
	if (count == 0)
		return;
	assert(pos + count <= m_Items.size());
	Size i;
	for (i = pos; i < pos + count; ++i) {
		RangeItem * pItem = m_Items[i];
		pItem->m_pRange = 0;
		pItem->m_Index = -1;
		pItem->m_Offset = 0;
	}
	m_Items.erase(m_Items.begin() + pos, m_Items.begin() + pos + count);
	InvalidateItems(pos);
}

void Range::Clear()
{
	Remove(0, GetItemCount());
}

Span Range::GetItemBounds(const RangeItem & item) const
{
	assert(item.m_pRange == this);
	if (!IsValid())
		const_cast<Range &>(* this).UpdateItems();
	return Span(0, int(item.m_Size)).Offset(GetOffset() + int(item.m_Offset));
}

Size Range::GetItemIndex(const RangeItem & item) const
{
	assert(item.m_pRange == this);
	if (!IsValid())
		const_cast<Range &>(* this).UpdateItems();
	return item.m_Index;
}

void Range::InvalidateItems(Size from)
{
	m_Invalid = true;
	if (from > m_Items.size())
		from = m_Items.size();
	if (from < m_InvalidRow) {
		m_InvalidRow = from;
		if (m_InvalidRow == 0)
			InvalidateSpan(Span(GetOffset(), INT_MAX));
		else {
			RangeItem * pValid = m_Items[m_InvalidRow - 1];
			InvalidateSpan(
				Span(GetOffset() + int(pValid->m_Offset + pValid->m_Size), INT_MAX)
			);
		}
	}
}

bool Range::IsValid() const
{
	return !m_Invalid;
}

void Range::UpdateItems()
{
	if (IsValid())
		return;
	if (m_Items.empty()) {
		m_Size = 0;
		m_InvalidRow = 0;
		m_Invalid = false;
		return;
	}
	Size offset = 0;
	if (m_InvalidRow > 0) {
		RangeItem * pValid = m_Items[m_InvalidRow - 1];
		offset = pValid->m_Offset + pValid->m_Size;
	}
	Size i;
	for (i = m_InvalidRow; i < m_Items.size(); ++i) {
		RangeItem * pItem = m_Items[i];
		pItem->m_Index = i;
		pItem->m_Offset = offset;
		offset += pItem->m_Size;
	}
	m_Invalid = false;
	for (i = m_InvalidRow; i < m_Items.size(); ++i)
		m_Items[i]->BoundsChanged();
	m_InvalidRow = m_Items.size();
	if (m_Size != offset) {
		m_Size = offset;
		SizeChanged();
	}
}

// RangeItem

RangeItem::RangeItem() :
	m_pRange(0), m_Index(-1), m_Size(0), m_Offset(0)
{
}

Span RangeItem::GetBounds() const
{
	if (m_pRange == 0)
		return Span(0, int(m_Size));
	else
		return m_pRange->GetItemBounds(* this);
}

int RangeItem::GetIndex() const
{
	if (m_pRange == 0)
		return -1;
	else
		return int(m_pRange->GetItemIndex(* this));
}

void RangeItem::Resize(Size s)
{
	if (s != m_Size) {
		m_Size = s;
		if (m_pRange != 0)
			m_pRange->InvalidateItems(m_Index);
	}
}

} // My

